﻿using System;
using System.Threading;

namespace Devtm.ServiceModel.Discovery.Result
{


    abstract class AsyncResult : IAsyncResult
    {


        #region private

        AsyncCallback callback;
        bool completedSynchronously;
        bool endCalled;
        Exception exception;
        bool isCompleted;
        ManualResetEvent manualResetEvent;
        object state;
        object thisLock;

        #endregion


        #region Ctor

        protected AsyncResult(AsyncCallback callback, object state)
        {
            this.callback = callback;
            this.state = state;
            this.thisLock = new object();
        }

        #endregion


        public object AsyncState
        {
            get
            {
                return state;
            }
        }


        public WaitHandle AsyncWaitHandle
        {
            get
            {
                if (manualResetEvent != null)
                {
                    return manualResetEvent;
                }
                lock (ThisLock)
                {
                    if (manualResetEvent == null)
                    {
                        manualResetEvent = new ManualResetEvent(isCompleted);
                    }
                }
                return manualResetEvent;
            }
        }


        public bool CompletedSynchronously
        {
            get
            {
                return completedSynchronously;
            }
        }


        public bool IsCompleted
        {
            get
            {
                return isCompleted;
            }
        }


        object ThisLock
        {
            get
            {
                return this.thisLock;
            }
        }


        protected static TAsyncResult End<TAsyncResult>(IAsyncResult result)
            where TAsyncResult : AsyncResult
        {

            if (result == null)
                throw new ArgumentNullException("result");
            

            TAsyncResult asyncResult = result as TAsyncResult;

            if (asyncResult == null)
                throw new ArgumentException("Invalid async result.", "result");
            

            if (asyncResult.endCalled)
                throw new InvalidOperationException("Async object already ended.");
            

            asyncResult.endCalled = true;


            if (!asyncResult.isCompleted)            
                asyncResult.AsyncWaitHandle.WaitOne();
            

            if (asyncResult.manualResetEvent != null)            
                asyncResult.manualResetEvent.Close();
            

            if (asyncResult.exception != null)
                throw asyncResult.exception;

            return asyncResult;
        }


        protected void Complete(bool completedSynchronously)
        {
            if (isCompleted)
                throw new InvalidOperationException("This async result is already completed.");

            this.completedSynchronously = completedSynchronously;

            if (completedSynchronously)
                this.isCompleted = true;

            else
                lock (ThisLock)
                {
                    this.isCompleted = true;
                    if (this.manualResetEvent != null)
                        this.manualResetEvent.Set();
        
                }

            if (callback != null)
                callback(this);
        
        }

        protected void Complete(bool completedSynchronously, Exception exception)
        {
            this.exception = exception;
            Complete(completedSynchronously);
        }
    }



}
